#include "54func.h"
#include "clientFunc.h"
#define FILELIMIT 100000000

// 辅助函数
// recvn
int recvn(int sockfd, void *buf, int length)
{
    char *p = (char *)buf;
    while(length > 0)
    {
        ssize_t sret = recv(sockfd,p,length,0);
        ERROR_CHECK(sret,-1,"recv in recvn");
        if(sret == 0)
        {
            return -1;
        }
        p += sret;
        length -= sret;
    }
    return 0;
}
// 根据传入的路径(cmd_t结构体中的一个argv字符串,可相对可绝对)
// 获取真实的Linux绝对路径存入realPath中
int realPath(const char *argv, char *realPath)
{
    if(argv[0] != '/')
    {
        // 相对路径
        char cwd[1024] = {0};
        getcwd(cwd,sizeof(cwd));
        sprintf(realPath,"%s%s%s",cwd,"/",argv);
    }
    else
    {
        // 绝对路径
        sprintf(realPath,"%s",argv);
    }
    return 0;
}
// 在目录路径后添加文件名,fileName为目录路径,argv为文件路径
int concatFileName(const char *argv,char *fileName)
{
    int fileSize = strlen(fileName);
    int length = strlen(argv);
    while(length >= 0)
    {
        if(argv[length-1] != '/') { --length; }
        else { break; }
    }
    sprintf(fileName+fileSize,"%s",argv+length);
    return 0;
}



// 上传文件clientPuts
// 非mmap版本transFile
int transFile(int sockfd, int filefd)
{
    train_t train;
    if(recvMsg(sockfd,&train) == 0)
    {
        // 'e'处理报错
        return -1;
    }
    
    // 'o'正常操作
    // 先收lseek长度
    recvn(sockfd,&train.length,sizeof(train.length));
    recvn(sockfd,train.data,train.length);
    // 断点续传lseek
    off_t offset;
    memcpy(&offset,train.data,train.length);
    lseek(filefd,offset,SEEK_SET);
    // 传输文件
    while(1)
    {
        ssize_t sret = read(filefd,train.data,TRAINLEN);
        train.length = sret;
        ssize_t sret1 = send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
        if(sret == 0 || sret1 == -1)
        {
            // 发完空包或者触发SIGPIPE退出
            break;
        }
    }
    close(filefd);
    return 0;
}
// mmap版本transFile
int transFileMmap(int sockfd, int filefd, off_t fileSize)
{
    train_t train;
    if(recvMsg(sockfd,&train) == 0)
    {
        // 'e'处理报错
        return -1;
    }
    
    // 'o'正常操作
    // 先收lseek长度
    recvn(sockfd,&train.length,sizeof(train.length));
    recvn(sockfd,train.data,train.length);
    // 断点续传lseek
    off_t offset;
    memcpy(&offset,train.data,train.length);
    //lseek(filefd,offset,SEEK_SET);
    // 发送文件内容
    char *p = (char *)mmap(NULL,fileSize,PROT_READ|PROT_WRITE,MAP_SHARED,filefd,0);
    ERROR_CHECK(p,MAP_FAILED,"mmap");
    send(sockfd, p+offset, fileSize-offset, MSG_NOSIGNAL);
    munmap(p,fileSize);
    //sendfile(sockfd,filefd,NULL,fileSize);
    close(filefd);
    return 0;
}

// 发送文件,cmd_t获取指令及参数
int clientPuts(int sockfd, const cmd_t *pcmd)
{
    // 获取文件名
    char fileName[PATH_MAX] = {0};
    realPath(pcmd->argv1,fileName);
    // 获取文件大小
    int filefd = open(fileName,O_RDWR);
    ERROR_CHECK(filefd,-1,"open in clientPuts");
    struct stat statbuf;
    fstat(filefd,&statbuf);
    // 发送文件长度
    train_t train;
    train.length = sizeof(statbuf.st_size);
    memcpy(train.data,&statbuf.st_size,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    // 根据大小选择适合的发送版本
    if(statbuf.st_size > FILELIMIT)
    {
        transFileMmap(sockfd,filefd,statbuf.st_size);
    }
    else
    {
        transFile(sockfd,filefd);
    }
    return 0;
}



// 下载文件clientGets
// 非mmap版recvFile
int recvFile(int sockfd, const char* fileName)
{
    train_t train;
    // 文件已存在就续传，不存在则新建文件
    int filefd = open(fileName,O_RDWR|O_CREAT,0666);
    ERROR_CHECK(filefd,-1,"open in recvFile");
    printf("filefd = %d\n",filefd);
    // 告知Client正常执行以及需要lseek的长度
    successSend(sockfd);
    off_t offset = lseek(filefd,0,SEEK_END);
    ERROR_CHECK(offset,-1,"lseek in recvFile");
    train.length = sizeof(offset);
    memcpy(train.data,&offset,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    // 接收文件内容
    while(1)
    {
        // recv
        recvn(sockfd,&train.length,sizeof(train.length));
        if(train.length == 0)   // 读完会读到空包就退出循环
        {
            break;
        }
        recvn(sockfd,train.data,train.length);
        // write
        ssize_t sret = write(filefd,train.data,train.length);
        ERROR_CHECK(sret,-1,"write in recvFile");
    }
    close(filefd);
    return 0;
}

// 接收文件,cwd_t获取cwd,cmd_t获取指令及参数,包含断点续传
int clientGets(int sockfd, const cmd_t *pcmd)
{
    // 获取文件名
    char fileName[PATH_MAX] = {0};
    if(pcmd->argc == 1)
    {
        // 默认cwd路径
        char cwd[1024] = {0};
        getcwd(cwd,sizeof(cwd));
        sprintf(fileName,"%s%s",cwd,"/");
    }
    else if(pcmd->argc == 2)
    {
        // 指定路径
        realPath(pcmd->argv2,fileName);
    }
    // 判断目录是否存在，存在就接上文件名，否则发送报错信息
    if(pathJudge(fileName,S_IFDIR) == 1)
    {
        concatFileName(pcmd->argv1,fileName);
    }
    else
    {
        errorSend(sockfd,NULL);
        errorSend(sockfd,"目标路径不存在!");
        return -1;
    }
    printf("fileName = %s\n",fileName);
    // 接收文件大小
    train_t train;
    off_t fileSize;
    recvn(sockfd,&train.length,sizeof(train.length));
    recvn(sockfd,train.data,train.length);
    memcpy(&fileSize,train.data,train.length);
    printf("filesize = %ld\n",fileSize);
    // 非mmap接收文件
    recvFile(sockfd,fileName);
    return 0;
}

